home *** CD-ROM | disk | FTP | other *** search
/ FishMarket 1.0 / FishMarket v1.0.iso / fishies / 051-075 / disk_061 / microemacs / line.c < prev    next >
C/C++ Source or Header  |  1992-05-06  |  19KB  |  533 lines

  1. /*
  2.  * The functions in this file are a general set of line management utilities.
  3.  * They are the only routines that touch the text. They also touch the buffer
  4.  * and window structures, to make sure that the necessary updating gets done.
  5.  * There are routines in this file that handle the kill buffer too. It isn't
  6.  * here for any good reason.
  7.  *
  8.  * Note that this code only updates the dot and mark values in the window list.
  9.  * Since all the code acts on the current window, the buffer that we are
  10.  * editing must be being displayed, which means that "b_nwnd" is non zero,
  11.  * which means that the dot and mark values in the buffer headers are nonsense.
  12.  */
  13.  
  14. #include        <stdio.h>
  15. #include    "estruct.h"
  16. #include        "edef.h"
  17.  
  18. #if    MEGAMAX
  19. overlay "line"
  20. #endif
  21.  
  22. KILL *ykbuf;    /* ptr to current kill buffer chunk being yanked */
  23. int ykboff;    /* offset into that chunk */
  24.  
  25. /*
  26.  * This routine allocates a block of memory large enough to hold a LINE
  27.  * containing "used" characters. The block is always rounded up a bit. Return
  28.  * a pointer to the new block, or NULL if there isn't any memory left. Print a
  29.  * message in the message line if no space.
  30.  */
  31. LINE    *
  32. lalloc(used)
  33. register int    used;
  34. {
  35.         register LINE   *lp;
  36.         register int    size;
  37.     char *malloc();
  38.  
  39.         size = (used+NBLOCK-1) & ~(NBLOCK-1);
  40.         if (size == 0)                          /* Assume that an empty */
  41.                 size = NBLOCK;                  /* line is for type-in. */
  42.         if ((lp = (LINE *) malloc(sizeof(LINE)+size)) == NULL) {
  43.                 mlwrite("Cannot allocate %d bytes", size);
  44.                 return (NULL);
  45.         }
  46.         lp->l_size = size;
  47.         lp->l_used = used;
  48.         return (lp);
  49. }
  50.  
  51. /*
  52.  * Delete line "lp". Fix all of the links that might point at it (they are
  53.  * moved to offset 0 of the next line. Unlink the line from whatever buffer it
  54.  * might be in. Release the memory. The buffers are updated too; the magic
  55.  * conditions described in the above comments don't hold here.
  56.  */
  57. lfree(lp)
  58. register LINE   *lp;
  59. {
  60.         register BUFFER *bp;
  61.         register WINDOW *wp;
  62.  
  63.         wp = wheadp;
  64.         while (wp != NULL) {
  65.                 if (wp->w_linep == lp)
  66.                         wp->w_linep = lp->l_fp;
  67.                 if (wp->w_dotp  == lp) {
  68.                         wp->w_dotp  = lp->l_fp;
  69.                         wp->w_doto  = 0;
  70.                 }
  71.                 if (wp->w_markp == lp) {
  72.                         wp->w_markp = lp->l_fp;
  73.                         wp->w_marko = 0;
  74.                 }
  75.                 wp = wp->w_wndp;
  76.         }
  77.         bp = bheadp;
  78.         while (bp != NULL) {
  79.                 if (bp->b_nwnd == 0) {
  80.                         if (bp->b_dotp  == lp) {
  81.                                 bp->b_dotp = lp->l_fp;
  82.                                 bp->b_doto = 0;
  83.                         }
  84.                         if (bp->b_markp == lp) {
  85.                                 bp->b_markp = lp->l_fp;
  86.                                 bp->b_marko = 0;
  87.                         }
  88.                 }
  89.                 bp = bp->b_bufp;
  90.         }
  91.         lp->l_bp->l_fp = lp->l_fp;
  92.         lp->l_fp->l_bp = lp->l_bp;
  93.         free((char *) lp);
  94. }
  95.  
  96. /*
  97.  * This routine gets called when a character is changed in place in the current
  98.  * buffer. It updates all of the required flags in the buffer and window
  99.  * system. The flag used is passed as an argument; if the buffer is being
  100.  * displayed in more than 1 window we change EDIT t HARD. Set MODE if the
  101.  * mode line needs to be updated (the "*" has to be set).
  102.  */
  103. lchange(flag)
  104. register int    flag;
  105. {
  106.         register WINDOW *wp;
  107.  
  108.         if (curbp->b_nwnd != 1)                 /* Ensure hard.         */
  109.                 flag = WFHARD;
  110.         if ((curbp->b_flag&BFCHG) == 0) {       /* First change, so     */
  111.                 flag |= WFMODE;                 /* update mode lines.   */
  112.                 curbp->b_flag |= BFCHG;
  113.         }
  114.         wp = wheadp;
  115.         while (wp != NULL) {
  116.                 if (wp->w_bufp == curbp)
  117.                         wp->w_flag |= flag;
  118.                 wp = wp->w_wndp;
  119.         }
  120. }
  121.  
  122. insspace(f, n)    /* insert spaces forward into text */
  123.  
  124. int f, n;    /* default flag and numeric argument */
  125.  
  126. {
  127.     linsert(n, ' ');
  128.     backchar(f, n);
  129. }
  130.  
  131. /*
  132.  * Insert "n" copies of the character "c" at the current location of dot. In
  133.  * the easy case all that happens is the text is stored in the line. In the
  134.  * hard case, the line has to be reallocated. When the window list is updated,
  135.  * take special care; I screwed it up once. You always update dot in the
  136.  * current window. You update mark, and a dot in another window, if it is
  137.  * greater than the place where you did the insert. Return TRUE if all is
  138.  * well, and FALSE on errors.
  139.  */
  140. linsert(n, c)
  141. {
  142.         register char   *cp1;
  143.         register char   *cp2;
  144.         register LINE   *lp1;
  145.         register LINE   *lp2;
  146.         register LINE   *lp3;
  147.         register int    doto;
  148.         register int    i;
  149.         register WINDOW *wp;
  150.  
  151.     if (curbp->b_mode&MDVIEW)    /* don't allow this command if    */
  152.         return(rdonly());    /* we are in read only mode    */
  153.         lchange(WFEDIT);
  154.         lp1 = curwp->w_dotp;                    /* Current line         */
  155.         if (lp1 == curbp->b_linep) {            /* At the end: special  */
  156.                 if (curwp->w_doto != 0) {
  157.                         mlwrite("bug: linsert");
  158.                         return (FALSE);
  159.                 }
  160.                 if ((lp2=lalloc(n)) == NULL)    /* Allocate new line    */
  161.                         return (FALSE);
  162.                 lp3 = lp1->l_bp;                /* Previous line        */
  163.                 lp3->l_fp = lp2;                /* Link in              */
  164.                 lp2->l_fp = lp1;
  165.                 lp1->l_bp = lp2;
  166.                 lp2->l_bp = lp3;
  167.                 for (i=0; i<n; ++i)
  168.                         lp2->l_text[i] = c;
  169.                 curwp->w_dotp = lp2;
  170.                 curwp->w_doto = n;
  171.                 return (TRUE);
  172.         }
  173.         doto = curwp->w_doto;                   /* Save for later.      */
  174.         if (lp1->l_used+n > lp1->l_size) {      /* Hard: reallocate     */
  175.                 if ((lp2=lalloc(lp1->l_used+n)) == NULL)
  176.                         return (FALSE);
  177.                 cp1 = &lp1->l_text[0];
  178.                 cp2 = &lp2->l_text[0];
  179.                 while (cp1 != &lp1->l_text[doto])
  180.                         *cp2++ = *cp1++;
  181.                 cp2 += n;
  182.                 while (cp1 != &lp1->l_text[lp1->l_used])
  183.                         *cp2++ = *cp1++;
  184.                 lp1->l_bp->l_fp = lp2;
  185.                 lp2->l_fp = lp1->l_fp;
  186.                 lp1->l_fp->l_bp = lp2;
  187.                 lp2->l_bp = lp1->l_bp;
  188.                 free((char *) lp1);
  189.         } else {                                /* Easy: in place       */
  190.                 lp2 = lp1;                      /* Pretend new line     */
  191.                 lp2->l_used += n;
  192.                 cp2 = &lp1->l_text[lp1->l_used];
  193.                 cp1 = cp2-n;
  194.                 while (cp1 != &lp1->l_text[doto])
  195.                         *--cp2 = *--cp1;
  196.         }
  197.         for (i=0; i<n; ++i)                     /* Add the characters   */
  198.                 lp2->l_text[doto+i] = c;
  199.         wp = wheadp;                            /* Update windows       */
  200.         while (wp != NULL) {
  201.                 if (wp->w_linep == lp1)
  202.                         wp->w_linep = lp2;
  203.                 if (wp->w_dotp == lp1) {
  204.                         wp->w_dotp = lp2;
  205.                         if (wp==curwp || wp->w_doto>doto)
  206.                                 wp->w_doto += n;
  207.                 }
  208.                 if (wp->w_markp == lp1) {
  209.                         wp->w_markp = lp2;
  210.                         if (wp->w_marko > doto)
  211.                                 wp->w_marko += n;
  212.                 }
  213.                 wp = wp->w_wndp;
  214.         }
  215.         return (TRUE);
  216. }
  217.  
  218. /*
  219.  * Insert a newline into the buffer at the current location of dot in the
  220.  * current window. The funny ass-backwards way it does things is not a botch;
  221.  * it just makes the last line in the file not a special case. Return TRUE if
  222.  * everything works out and FALSE on error (memory allocation failure). The
  223.  * update of dot and mark is a bit easier then in the above case, because the
  224.  * split forces more updating.
  225.  */
  226. lnewline()
  227. {
  228.         register char   *cp1;
  229.         register char   *cp2;
  230.         register LINE   *lp1;
  231.         register LINE   *lp2;
  232.         register int    doto;
  233.         register WINDOW *wp;
  234.  
  235.     if (curbp->b_mode&MDVIEW)    /* don't allow this command if    */
  236.         return(rdonly());    /* we are in read only mode    */
  237.         lchange(WFHARD);
  238.         lp1  = curwp->w_dotp;                   /* Get the address and  */
  239.         doto = curwp->w_doto;                   /* offset of "."        */
  240.         if ((lp2=lalloc(doto)) == NULL)         /* New first half line  */
  241.                 return (FALSE);
  242.         cp1 = &lp1->l_text[0];                  /* Shuffle text around  */
  243.         cp2 = &lp2->l_text[0];
  244.         while (cp1 != &lp1->l_text[doto])
  245.                 *cp2++ = *cp1++;
  246.         cp2 = &lp1->l_text[0];
  247.         while (cp1 != &lp1->l_text[lp1->l_used])
  248.                 *cp2++ = *cp1++;
  249.         lp1->l_used -= doto;
  250.         lp2->l_bp = lp1->l_bp;
  251.         lp1->l_bp = lp2;
  252.         lp2->l_bp->l_fp = lp2;
  253.         lp2->l_fp = lp1;
  254.         wp = wheadp;                            /* Windows              */
  255.         while (wp != NULL) {
  256.                 if (wp->w_linep == lp1)
  257.                         wp->w_linep = lp2;
  258.                 if (wp->w_dotp == lp1) {
  259.                         if (wp->w_doto < doto)
  260.                                 wp->w_dotp = lp2;
  261.                         else
  262.                                 wp->w_doto -= doto;
  263.                 }
  264.                 if (wp->w_markp == lp1) {
  265.                         if (wp->w_marko < doto)
  266.                                 wp->w_markp = lp2;
  267.                         else
  268.                                 wp->w_marko -= doto;
  269.                 }
  270.                 wp = wp->w_wndp;
  271.         }
  272.         return (TRUE);
  273. }
  274.  
  275. /*
  276.  * This function deletes "n" bytes, starting at dot. It understands how do deal
  277.  * with end of lines, etc. It returns TRUE if all of the characters were
  278.  * deleted, and FALSE if they were not (because dot ran into the end of the
  279.  * buffer. The "kflag" is TRUE if the text should be put in the kill buffer.
  280.  */
  281. ldelete(n, kflag)
  282.  
  283. long n;        /* # of chars to delete */
  284. int kflag;    /* put killed text in kill buffer flag */
  285.  
  286. {
  287.         register char   *cp1;
  288.         register char   *cp2;
  289.         register LINE   *dotp;
  290.         register int    doto;
  291.         register int    chunk;
  292.         register WINDOW *wp;
  293.  
  294.     if (curbp->b_mode&MDVIEW)    /* don't allow this command if    */
  295.         return(rdonly());    /* we are in read only mode    */
  296.         while (n != 0) {
  297.                 dotp = curwp->w_dotp;
  298.                 doto = curwp->w_doto;
  299.                 if (dotp == curbp->b_linep)     /* Hit end of buffer.   */
  300.                         return (FALSE);
  301.                 chunk = dotp->l_used-doto;      /* Size of chunk.       */
  302.                 if (chunk > n)
  303.                         chunk = n;
  304.                 if (chunk == 0) {               /* End of line, merge.  */
  305.                         lchange(WFHARD);
  306.                         if (ldelnewline() == FALSE
  307.                         || (kflag!=FALSE && kinsert('\n')==FALSE))
  308.                                 return (FALSE);
  309.                         --n;
  310.                         continue;
  311.                 }
  312.                 lchange(WFEDIT);
  313.                 cp1 = &dotp->l_text[doto];      /* Scrunch text.        */
  314.                 cp2 = cp1 + chunk;
  315.                 if (kflag != FALSE) {           /* Kill?                */
  316.                         while (cp1 != cp2) {
  317.                                 if (kinsert(*cp1) == FALSE)
  318.                                         return (FALSE);
  319.                                 ++cp1;
  320.                         }
  321.                         cp1 = &dotp->l_text[doto];
  322.                 }
  323.                 while (cp2 != &dotp->l_text[dotp->l_used])
  324.                         *cp1++ = *cp2++;
  325.                 dotp->l_used -= chunk;
  326.                 wp = wheadp;                    /* Fix windows          */
  327.                 while (wp != NULL) {
  328.                         if (wp->w_dotp==dotp && wp->w_doto>=doto) {
  329.                                 wp->w_doto -= chunk;
  330.                                 if (wp->w_doto < doto)
  331.                                         wp->w_doto = doto;
  332.                         }
  333.                         if (wp->w_markp==dotp && wp->w_marko>=doto) {
  334.                                 wp->w_marko -= chunk;
  335.                                 if (wp->w_marko < doto)
  336.                                         wp->w_marko = doto;
  337.                         }
  338.                         wp = wp->w_wndp;
  339.                 }
  340.                 n -= chunk;
  341.         }
  342.         return (TRUE);
  343. }
  344.  
  345. /*
  346.  * Delete a newline. Join the current line with the next line. If the next line
  347.  * is the magic header line always return TRUE; merging the last line with the
  348.  * header line can be thought of as always being a successful operation, even
  349.  * if nothing is done, and this makes the kill buffer work "right". Easy cases
  350.  * can be done by shuffling data around. Hard cases require that lines be moved
  351.  * about in memory. Return FALSE on error and TRUE if all looks ok. Called by
  352.  * "ldelete" only.
  353.  */
  354. ldelnewline()
  355. {
  356.         register char   *cp1;
  357.         register char   *cp2;
  358.         register LINE   *lp1;
  359.         register LINE   *lp2;
  360.         register LINE   *lp3;
  361.         register WINDOW *wp;
  362.  
  363.     if (curbp->b_mode&MDVIEW)    /* don't allow this command if    */
  364.         return(rdonly());    /* we are in read only mode    */
  365.         lp1 = curwp->w_dotp;
  366.         lp2 = lp1->l_fp;
  367.         if (lp2 == curbp->b_linep) {            /* At the buffer end.   */
  368.                 if (lp1->l_used == 0)           /* Blank line.          */
  369.                         lfree(lp1);
  370.                 return (TRUE);
  371.         }
  372.         if (lp2->l_used <= lp1->l_size-lp1->l_used) {
  373.                 cp1 = &lp1->l_text[lp1->l_used];
  374.                 cp2 = &lp2->l_text[0];
  375.                 while (cp2 != &lp2->l_text[lp2->l_used])
  376.                         *cp1++ = *cp2++;
  377.                 wp = wheadp;
  378.                 while (wp != NULL) {
  379.                         if (wp->w_linep == lp2)
  380.                                 wp->w_linep = lp1;
  381.                         if (wp->w_dotp == lp2) {
  382.                                 wp->w_dotp  = lp1;
  383.                                 wp->w_doto += lp1->l_used;
  384.                         }
  385.                         if (wp->w_markp == lp2) {
  386.                                 wp->w_markp  = lp1;
  387.                                 wp->w_marko += lp1->l_used;
  388.                         }
  389.                         wp = wp->w_wndp;
  390.                 }
  391.                 lp1->l_used += lp2->l_used;
  392.                 lp1->l_fp = lp2->l_fp;
  393.                 lp2->l_fp->l_bp = lp1;
  394.                 free((char *) lp2);
  395.                 return (TRUE);
  396.         }
  397.         if ((lp3=lalloc(lp1->l_used+lp2->l_used)) == NULL)
  398.                 return (FALSE);
  399.         cp1 = &lp1->l_text[0];
  400.         cp2 = &lp3->l_text[0];
  401.         while (cp1 != &lp1->l_text[lp1->l_used])
  402.                 *cp2++ = *cp1++;
  403.         cp1 = &lp2->l_text[0];
  404.         while (cp1 != &lp2->l_text[lp2->l_used])
  405.                 *cp2++ = *cp1++;
  406.         lp1->l_bp->l_fp = lp3;
  407.         lp3->l_fp = lp2->l_fp;
  408.         lp2->l_fp->l_bp = lp3;
  409.         lp3->l_bp = lp1->l_bp;
  410.         wp = wheadp;
  411.         while (wp != NULL) {
  412.                 if (wp->w_linep==lp1 || wp->w_linep==lp2)
  413.                         wp->w_linep = lp3;
  414.                 if (wp->w_dotp == lp1)
  415.                         wp->w_dotp  = lp3;
  416.                 else if (wp->w_dotp == lp2) {
  417.                         wp->w_dotp  = lp3;
  418.                         wp->w_doto += lp1->l_used;
  419.                 }
  420.                 if (wp->w_markp == lp1)
  421.                         wp->w_markp  = lp3;
  422.                 else if (wp->w_markp == lp2) {
  423.                         wp->w_markp  = lp3;
  424.                         wp->w_marko += lp1->l_used;
  425.                 }
  426.                 wp = wp->w_wndp;
  427.         }
  428.         free((char *) lp1);
  429.         free((char *) lp2);
  430.         return (TRUE);
  431. }
  432.  
  433. /*
  434.  * Delete all of the text saved in the kill buffer. Called by commands when a
  435.  * new kill context is being created. The kill buffer array is released, just
  436.  * in case the buffer has grown to immense size. No errors.
  437.  */
  438. kdelete()
  439. {
  440.     KILL *kp;    /* ptr to scan kill buffer chunk list */
  441.  
  442.         if (kbufh != NULL) {
  443.  
  444.         /* first, delete all the chunks */
  445.             kbufp = kbufh;
  446.             while (kbufp != NULL) {
  447.                 kp = kbufp->d_next;
  448.                 free(kbufp);
  449.                 kbufp = kp;
  450.             }
  451.  
  452.         /* and reset all the kill buffer pointers */
  453.         kbufh = kbufp = NULL;
  454.         kused = KBLOCK;                
  455.         }
  456. }
  457.  
  458. /*
  459.  * Insert a character to the kill buffer, allocating new chunks as needed.
  460.  * Return TRUE if all is well, and FALSE on errors.
  461.  */
  462.  
  463. kinsert(c)
  464.  
  465. int c;        /* character to insert in the kill buffer */
  466.  
  467. {
  468.     KILL *nchunk;    /* ptr to newly malloced chunk */
  469.  
  470.     /* check to see if we need a new chunk */
  471.     if (kused >= KBLOCK) {
  472.         if ((nchunk = (KILL *)malloc(sizeof(KILL))) == NULL)
  473.             return(FALSE);
  474.         if (kbufh == NULL)    /* set head ptr if first time */
  475.             kbufh = nchunk;
  476.         if (kbufp != NULL)    /* point the current to this new one */
  477.             kbufp->d_next = nchunk;
  478.         kbufp = nchunk;
  479.         kbufp->d_next = NULL;
  480.         kused = 0;
  481.     }
  482.  
  483.     /* and now insert the character */
  484.     kbufp->d_chunk[kused++] = c;
  485.     return(TRUE);
  486. }
  487.  
  488. /*
  489.  * Yank text back from the kill buffer. This is really easy. All of the work
  490.  * is done by the standard insert routines. All you do is run the loop, and
  491.  * check for errors. Bound to "C-Y".
  492.  */
  493. yank(f, n)
  494. {
  495.         register int    c;
  496.         register int    i;
  497.     register char    *sp;    /* pointer into string to insert */
  498.     KILL *kp;        /* pointer into kill buffer */
  499.  
  500.     if (curbp->b_mode&MDVIEW)    /* don't allow this command if    */
  501.         return(rdonly());    /* we are in read only mode    */
  502.         if (n < 0)
  503.                 return (FALSE);
  504.     /* make sure there is something to yank */
  505.     if (kbufh == NULL)
  506.         return(TRUE);        /* not an error, just nothing */
  507.  
  508.     /* for each time.... */
  509.         while (n--) {
  510.         kp = kbufh;
  511.         while (kp != NULL) {
  512.             if (kp->d_next == NULL)
  513.                 i = kused;
  514.             else
  515.                 i = KBLOCK;
  516.             sp = kp->d_chunk;
  517.             while (i--) {
  518.                             if ((c = *sp++) == '\n') {
  519.                                     if (lnewline(FALSE, 1) == FALSE)
  520.                                             return (FALSE);
  521.                             } else {
  522.                                     if (linsert(1, c) == FALSE)
  523.                                             return (FALSE);
  524.                             }
  525.                     }
  526.                     kp = kp->d_next;
  527.                 }
  528.         }
  529.         return (TRUE);
  530. }
  531.  
  532.  
  533.